package arconplugin.handlers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.uml2.uml.Model;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;

import arconplugin.Activator;
import arconplugin.preferences.PreferenceConstants;
import arconplugin.preferences.PreferenceInitializer;

/**
 * Our sample handler extends AbstractHandler, an IHandler base class.
 * @see org.eclipse.core.commands.IHandler
 * @see org.eclipse.core.commands.AbstractHandler
 */
public class RunArconHandler extends ConnectFilesHandler {
	private final String uml_parameter = "UML";
	private final String debug_parameter = "debug";
	
	/**
	 * The constructor.
	 */
	public RunArconHandler() {
	}

	/**
	 * The command has been executed, so extract extract the needed information
	 * from the application context.
	 */
	public Object execute(ExecutionEvent event) throws ExecutionException {
		IFile file = getActiveFile();	
		if(file == null) {
			debugPrint("No file");
			return null;
		}
		
		checkModels(file);
		return null;
	}
	
	/**
	 * Find the Architecture model and System model, then call ArCon for the check.
	 * @param file - Active model file in the editor
	 */
	protected void checkModels(IFile active_model)
	{
		//remove extension since the given file will be the diagram and not the .uml file
		IPath active = active_model.getLocation().removeFileExtension(); 
		ModelType other = null;
		//first try to find the other model using naming convention, if there exist no saved settings
		if(SavedInfoFileExist(active)==false) {
			other = getConnectedModelByNameConvention(active_model); 
		}
		//If saved settings exist or if there exist no model which follows the naming convention, ask the user to select the model instead.
		if(other==null) { 
			other = GetConnectedModel(active);
		}
		
		
		
		IPath system,arch;
		system = other.isSystemModel() ? other.getPath() : active;
		arch = other.isSystemModel() ? active : other.getPath();
		
		system = system.addFileExtension(uml_extension);
		arch = arch.addFileExtension(uml_extension);
		
		debugPrint("arch: "+arch);
		debugPrint("system: "+system);
		Model model = getModel(arch.toOSString());
		
		if(model==null) {
			printError("Architecture model could not be loaded!");
			return;
		}
		String name = model.getName();
		debugPrint("Architecture model name: "+name);
		
		model = getModel(system.toOSString());
		if(model==null) {
			printError("System model could not be loaded!");
			return;
		}
		name = model.getName();
		debugPrint("System model name: "+name);
		
		executeArCon(arch.toOSString(), system.toOSString());
	}
	
	/**
	 * Execute ArCon given the model paths
	 * @param arch_model - Architecture uml model
	 * @param system_model - Realizing system uml model
	 * @return success on executing ArCon
	 */
	private boolean executeArCon(String arch_model, String system_model) {
		String line;
		//OutputStream stdin = null;
		InputStream stderr = null;
		InputStream stdout = null;
		String arcon_path = GetArconPath();
		if(arcon_path==null) {
			printError("Could not find a valid path to ArCon.");
			return false;
		}
		
		Process process;
		try {
			doPrint("Launching ArCon...");
			debugPrint(arcon_path);
			
			IPreferenceStore store = Activator.getDefault().getPreferenceStore();
			int debug_level = store.getInt(PreferenceConstants.P_DEBUG_LEVEL);
			
			String[] cmdarray = { arcon_path, uml_parameter, arch_model, system_model, debug_parameter, String.valueOf(debug_level) };
			process = Runtime.getRuntime().exec(cmdarray);
			
			stderr = process.getErrorStream();
			stdout = process.getInputStream();
				
			//clean up
			BufferedReader brCleanUp = new BufferedReader( new InputStreamReader(stdout));
			while((line = brCleanUp.readLine()) != null) {
				doPrint("[ArCon] " + line);
				//TODO: add output from Arcon to some useful place.
			}
			brCleanUp.close();
			
			brCleanUp = new BufferedReader( new InputStreamReader(stderr));
			while((line = brCleanUp.readLine()) != null) {
				printError("[ArCon Error] " + line);
			}
			brCleanUp.close();
			return true;
		} catch (IOException e) {
			printError("Could not launch ArCon");
			doPrint(e.getMessage());
		}
		
		
		return false;
	}
	
	/**
	 * Return the connected Model using naming convention
	 * @param model_file
	 * @return
	 */
	private ModelType getConnectedModelByNameConvention(IFile model_file) {
		IPath path = model_file.getLocation().removeFileExtension(); //remove extension since it will be the diagram and not the .uml
		//out("path without extension: "+path);
		String file_name = path.lastSegment();
		path = model_file.getLocation().removeLastSegments(1); //remove the name
		//out("path: "+path);
		
		ModelType model = null;
		
		
		IPreferenceStore store = Activator.getDefault().getPreferenceStore();
		String architecture_postfix = store.getString(PreferenceConstants.P_ARCH_POSTFIX);
		String system_postfix = store.getString(PreferenceConstants.P_SYSTEM_POSTFIX);
		boolean b_postfix = store.getString(PreferenceConstants.P_AFFIX_TYPE).equals(PreferenceInitializer.POSTFIX);
		
		//Note: The other model is of the opposite type of what the file_name (active file) is. 
		//So if the active file is a system_model we want the connected model to arch_model.
		if(file_name.contains(system_postfix)) { 
			model = new ArchRulesModel();
		} else if(file_name.contains(architecture_postfix)) {
			model = new SystemModel();
		} else {
			return null;
		}
		
		String simple_model_name = getSimpleName(file_name);
		String affix = model.isSystemModel() ? system_postfix : architecture_postfix;
		
		if(b_postfix) {
			model.setPath(path.append(simple_model_name+affix));
		} else {
			model.setPath(path.append(affix+simple_model_name));
		}	
		
		if(!model.exists()) //check if the other model exist
			return null;
		return model;
	}
	
	/**
	 * Get the simple name of the model by removing the affix.
	 * @param file_name
	 * @return name without affix or null if it doesn't follow the convention
	 */
	private String getSimpleName(String file_name) {
		IPreferenceStore store = Activator.getDefault().getPreferenceStore();
		String architecture_postfix = store.getString(PreferenceConstants.P_ARCH_POSTFIX);
		String system_postfix = store.getString(PreferenceConstants.P_SYSTEM_POSTFIX);
		//boolean b_postfix = store.getString(PreferenceConstants.P_AFFIX_TYPE).equals(PreferenceInitializer.POSTFIX);
		
		if(file_name.contains(system_postfix)) {
			return file_name.replace(system_postfix, "");
		}
		if(file_name.contains(architecture_postfix)) {
			return file_name.replace(architecture_postfix, "");
		}
		//printError("Invalid naming convention on model: "+file_name);
		//printError("Valid naming "+(b_postfix?"postfix":"prefix")+":\n Architecture: "+architecture_postfix+"\n"+" System: "+system_postfix);
		return null;
	}
}
